.. _глава_49_start:

Перегрузка функций (Overloading)
================================

.. index::
    overloading


Определение нескольких функций с одним и тем же именем - это перегрузка функций. В
чтобы можно было различать эти функции, их параметры должны быть
разными.
В следующем коде есть несколько перегрузок функции info(), каждая из которых принимает
разные типы параметров:

.. code-block:: d

    import std.stdio;

    void info(double number)
    {
        writeln("Floating point: ", number);
    }

    void info(int number)
    {
        writeln("Integer : ", number);
    }

    void info(string str)
    {
        writeln("String : ", str);
    }

    void main()
    {
        info(1.2);
        info(3);
        info("hello");
    }

Хотя все функции называются info(), компилятор выбирает ту, которая
соответствует аргументу, который используется при вызове. Например, поскольку
литерал 1.2 имеет тип double, функция info(), принимающая double, вызывается для него
вызывается для нее.
Выбор функции для вызова осуществляется во время компиляции, что не всегда
что не всегда легко и понятно. Например, поскольку int может быть неявно преобразован как в
как в double, так и в real, компилятор не может решить, какую из функций вызывать
в следующей программе:

.. code-block:: d

    real sevenTimes(real value)
    {
        return 7 * value;
    }

    double sevenTimes(double value)
    {
        return 7 * value;
    }

    void main()
    {
        int value = 5;
        auto result = sevenTimes(value); // ← ошибка  компиляции
    }

*Примечание: Обычно нет необходимости писать отдельные функции, если тела функций
совершенно одинаковы. Позже в главе "Шаблоны" (стр. 399) мы увидим, как одно
определение может использоваться для нескольких типов.*
Однако если существует другая перегрузка функции, принимающая длинный параметр,
то двусмысленность будет устранена, поскольку long лучше подходит для int, чем
double или real:

.. code-block:: d

    long sevenTimes(long value)
    {
        return 7 * value;
    }
    // ...
    auto result = sevenTimes(value); // теперь компилируется


Разрешение имен функции при перегрузке
--------------------------------------

Компилятор выбирает вариант, который лучше всего соответствует аргументам. Это
называется разрешением перегрузки.
Хотя в большинстве случаев разрешение перегрузки просто и интуитивно понятно, иногда оно
иногда оно бывает сложным. Ниже приведены правила разрешения перегрузок. Они
в этой книге представлены в упрощенном виде.
Существует четыре состояния совпадения, перечисленные от худшего к лучшему:
* несоответствие
* совпадение через автоматическое преобразование типа
* совпадение через квалификацию const
* точное совпадение
При разрешении перегрузки компилятор учитывает все варианты функции.
Сначала он определяет состояние соответствия каждого параметра для каждой
перегрузки. Для каждого варианта наименьшее состояние совпадения среди параметров принимается
в качестве состояния соответствия этой перегрузки.
После определения всех состояний соответствия всех вариантов выбирается вариант
с наилучшим совпадением выбирается. Если существует более одной перегрузки, имеющей
то применяются более сложные правила разрешения. В этой книге я не буду
подробности этих правил в этой книге. Если ваша программа находится в ситуации, когда она
зависит от сложных правил разрешения перегрузок, это может быть признаком того, что
что пришло время изменить дизайн программы. Другой вариант - воспользоваться
другими возможностями D, например шаблонами. Еще более простой, но не всегда желательный
подход - полностью отказаться от перегрузки функций, называя
называя функции по-разному для каждого типа, например sevenTimes_real() и
sevenTimes_double().

Перегрузка функций для пользовательских типов
----------------------------------------------

Перегрузка функций полезна и для структур и классов. Кроме того,
неоднозначность разрешения перегрузки гораздо реже возникает при использовании пользовательских типов.
Давайте перегрузим функцию info(), приведенную выше, для некоторых типов, которые мы
определенных в главе "Structure"

.. code-block:: d

    struct TimeOfDay
    {
        int hour;
        int minute;
    }

    void info(TimeOfDay time)
    {
        writef("%02s:%02s", time.hour, time.minute);
    }

Эта перегрузка позволяет использовать объекты TimeOfDay в функции info(). В результате,
переменные пользовательских типов могут быть выведены точно так же, как и
фундаментальные типы:

.. code-block:: d

    auto breakfastTime = TimeOfDay(7, 0);
    info(breakfastTime);

Объекты TimeOfDay будут сопоставлены с этой перегрузкой info():

.. code-block:: console

    07:00

Ниже приведена перегрузка функции info() для типа Meeting:

.. code-block:: d

    struct Meeting
    {
        string topic;
        size_t attendanceCount;
        TimeOfDay start;
        TimeOfDay end;
    }

    void info(Meeting meeting)
    {
        info(meeting.start);
        write('-');
        info(meeting.end);
        writef(" \"%s\" meeting with %s attendees",
            meeting.topic,
            meeting.attendanceCount);
    }

Обратите внимание, что эта перегрузка использует уже определенную перегрузку для TimeOfDay.
Объекты встреч теперь можно выводить точно так же, как и фундаментальные
типы:

.. code-block:: d

    auto bikeRideMeeting = Meeting("Bike Ride", 3,
                                   TimeOfDay(9, 0),
                                   TimeOfDay(9, 10));
    info(bikeRideMeeting);

Вывод:

.. code-block:: console

    09:00-09:10 "Bike Ride" meeting with 3 attendees

Ограничения
-----------

Хотя перегрузка функции info(), о которой говорилось выше, очень удобна, этот метод
метод имеет некоторые ограничения:
* info() всегда печатает в stdout. Было бы полезнее, если бы он мог печатать в
любой файл. Один из способов добиться этого - передать поток вывода в качестве
например, для типа TimeOfDay:

.. code-block:: d

    void info(File file, TimeOfDay time)
    {
        file.writef("%02s:%02s", time.hour, time.minute);
    }

Это позволит печатать объекты TimeOfDay в любой файл, включая stdout:

.. code-block:: d

    info(stdout, breakfastTime);
    auto file = File("a_file", "w");
    info(file, breakfastTime);

*Примечание: Специальные объекты stdin, stdout и stderr имеют тип File.*

* Что еще более важно, info() не решает более общую проблему
  создания строкового представления переменных. Например, она не помогает
  при передаче объектов пользовательских типов в writeln():

.. code-block:: d

    writeln(breakfastTime); // Не полезно: печатает в общем формате

Приведенный выше код печатает объект в общем формате, который включает имя
типа и значения его членов, но не в том виде, который был бы полезен в
программе:

.. code-block:: console

    TimeOfDay(7, 0)

Было бы гораздо полезнее, если бы существовала функция, преобразующая
объекты TimeOfDay в строку в их специальном формате, как в "12:34". Мы рассмотрим
как определять строковые представления объектов struct в следующей главе.

Упражнения
----------

#. Перегрузите функцию info() и для следующих структур:

.. code-block:: d

    struct Meal
    {
        TimeOfDay time;
        string address;
    }

    struct DailyPlan
    {
        Meeting amMeeting;
        Meal lunch;
        Meeting pmMeeting;
    }

Поскольку у Meal есть только время начала, добавьте полтора часа, чтобы определить время окончания
время. Для этого можно использовать функцию addDuration(), которую мы определили ранее в
главе о структурах:

.. code-block:: d

    imeOfDay addDuration(TimeOfDay start,TimeOfDay duration)
    {
        TimeOfDay result;
        result.minute = start.minute + duration.minute;
        result.hour = start.hour + duration.hour;
        result.hour += result.minute / 60;
        result.minute %= 60;
        result.hour %= 24;
        return result;
    }


После того как время окончания работы объектов Meal будет рассчитано с помощью функции addDuration(), объекты DailyPlan
должны быть выведены объекты DailyPlan, как показано в следующем выводе:

.. code-block:: console

    10:30-11:45 "Bike Ride" meeting with 4 attendees
    12:30-14:00 Meal, Address: İstanbul
    15:30-17:30 "Budget" meeting with 8 attendees

:ref:`решения Перегрузка функций<решения Перегрузка функций>`


